Skip to content

Xcode15之前的处理方案

在SwiftUI中,特点之一就是能支持实时预览页面UI效果,从而提高开发效率。而使用UIKit开发的过程中,尽管可以好用Xib/Storyborad/@IBDesignable等看到开发效果,但是开发体验不好。

我们知道,如果想在SwiftUI中嵌入UIKit的页面,需要实现UIViewRepresentable协议。因此实现了该协议,基本就能实现实时预览UIKit的效果,我们需要做的只是做一些简单的封装,简化使用。

预览UIView

swift
import SwiftUI
struct UIViewPreview<View: UIView>: UIViewRepresentable {
    let view: View

    init(_ builder: @escaping () -> View) {
        view = builder()
    }
    // MARK: UIViewRepresentable
    func makeUIView(context: Context) -> UIView {
        return view
    }
    func updateUIView(_ view: UIView, context: Context) {
        view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        view.setContentHuggingPriority(.defaultHigh, for: .vertical)
    }
}
struct UIView_Previews: PreviewProvider {
    static var previews: some View {
        UIViewPreview {
            let vc = UIView()
            vc.backgroundColor = .green
            return vc
        }
    }
}
import SwiftUI
struct UIViewPreview<View: UIView>: UIViewRepresentable {
    let view: View

    init(_ builder: @escaping () -> View) {
        view = builder()
    }
    // MARK: UIViewRepresentable
    func makeUIView(context: Context) -> UIView {
        return view
    }
    func updateUIView(_ view: UIView, context: Context) {
        view.setContentHuggingPriority(.defaultHigh, for: .horizontal)
        view.setContentHuggingPriority(.defaultHigh, for: .vertical)
    }
}
struct UIView_Previews: PreviewProvider {
    static var previews: some View {
        UIViewPreview {
            let vc = UIView()
            vc.backgroundColor = .green
            return vc
        }
    }
}

截屏2024-01-11 14.18.45.png

预览Controller

swift
import SwiftUI
struct UIViewControllerPreview<ViewController: UIViewController>: UIViewControllerRepresentable {
    func updateUIViewController(_ uiViewController: ViewController, context: Context) {
    }
    
    let viewController: ViewController

    init(_ builder: @escaping () -> ViewController) {
        viewController = builder()
    }
    // MARK: - UIViewControllerRepresentable
    func makeUIViewController(context: Context) -> ViewController {
        viewController
    }
}

struct ControllerPreviews_Previews: PreviewProvider {
    static var previews: some View {
        UIViewControllerPreview {
            LoginViewController()
        }
    }
}
import SwiftUI
struct UIViewControllerPreview<ViewController: UIViewController>: UIViewControllerRepresentable {
    func updateUIViewController(_ uiViewController: ViewController, context: Context) {
    }
    
    let viewController: ViewController

    init(_ builder: @escaping () -> ViewController) {
        viewController = builder()
    }
    // MARK: - UIViewControllerRepresentable
    func makeUIViewController(context: Context) -> ViewController {
        viewController
    }
}

struct ControllerPreviews_Previews: PreviewProvider {
    static var previews: some View {
        UIViewControllerPreview {
            LoginViewController()
        }
    }
}

报错处理

老项目报错:

c
    |  RemoteHumanReadableError: 未能完成该操作。Transaction failed. Process failed to launch. (process launch failed)
    |  
    |  BSTransactionError (1):
    |  ==transaction: <FBApplicationProcessLaunchTransaction: 0x600003b0d340>
    |  ==error-description: Process failed to launch.
    |  ==precipitating-error: Error Domain=FBProcessExit Code=64 "The process failed to launch." UserInfo={NSLocalizedFailureReason=The process failed to launch., BSErrorCodeDescription=launch-failed, NSUnderlyingError=0x600000c48b10 {Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0x600000c48e70 {Error Domain=NSPOSIXErrorDomain Code=111 "Unknown error: 111" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}}}
    |  ==error-reason: process launch failed
    |  ==NSLocalizedFailureReason: Transaction failed. Process failed to launch. (process launch failed)
    |  RemoteHumanReadableError: 未能完成该操作。Transaction failed. Process failed to launch. (process launch failed)
    |  
    |  BSTransactionError (1):
    |  ==transaction: <FBApplicationProcessLaunchTransaction: 0x600003b0d340>
    |  ==error-description: Process failed to launch.
    |  ==precipitating-error: Error Domain=FBProcessExit Code=64 "The process failed to launch." UserInfo={NSLocalizedFailureReason=The process failed to launch., BSErrorCodeDescription=launch-failed, NSUnderlyingError=0x600000c48b10 {Error Domain=RBSRequestErrorDomain Code=5 "Launch failed." UserInfo={NSLocalizedFailureReason=Launch failed., NSUnderlyingError=0x600000c48e70 {Error Domain=NSPOSIXErrorDomain Code=111 "Unknown error: 111" UserInfo={NSLocalizedDescription=Launchd job spawn failed}}}}}
    |  ==error-reason: process launch failed
    |  ==NSLocalizedFailureReason: Transaction failed. Process failed to launch. (process launch failed)

解决方案: 勾选 Show Rosetta Destinations
截屏2024-01-11 16.40.47.png

测试效果: 可能是在老项目上运行的原因,非常卡顿。

Xcode15 宏 #Preview{}

使用宏#Preview{} 可以支持UIKit 和 SwiftUI 。 需要注意如果系统不是支持>=iOS17, 必须增加@available(iOS 17, *)

swift
import SwiftUI
@available(iOS 17, *)
#Preview {
    UISwitch()
}
import SwiftUI
@available(iOS 17, *)
#Preview {
    UISwitch()
}

通过抽离SwiftUI代码的方式提速

[通过抽离SwiftUI代码的方式提高预览速度]

实际问题

用SwiftUI来预览UIView页面,刷新慢,实际体验并不好。